﻿using Avalonia.Controls;
using HarfBuzzSharp;
using LightCAD.Core;
using LightCAD.Drawing;
using LightCAD.Runtime;
using LightCAD.Three;
using QdArch.ThreeUtils;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;

namespace QdArch
{
    public class StairAction : ElementAction
    {
        private PointInputer PointInputer { get; set; }

        public StairAction() { }
        public StairAction(DocumentRuntime docRt) : base(docRt) 
        {
            docRt.CommandCtrl.WriteInfo("命令：Stair");
        }
        internal static void Initilize()
        {
            ArchActions.StairAction = new StairAction();
            LcDocument.ElementActions.Add(ArchElementType.Stair, ArchActions.StairAction);
        }
        public async void ExecCreate(string[] args = null)
        {
            //1. 选择插入点
            this.PointInputer = new PointInputer(this.docRt);
            QdStairRef stairRef = null;
            var result = await this.PointInputer.Execute("插入点:");
            if (result == null || result.Value == null || !(result.Value is Vector2d)||this.PointInputer.isCancelled)
            {
                this.Cancel();
                goto End;
            }
            var stairName = "DogleggedStair";
            var stair = docRt.Document.Components.Find(ArchCategory.Stair.ToString(), stairName) as QdStair;
            if (stair == null)
            {
                stair = new QdStair(StairCategory.DogleggedStair.ToString(), stairName);
                docRt.Document.Components.Add(stair);
            }
            var instP = (Vector2d)result.Value;
            stairRef = this.docRt.Document.CreateObject<QdStairRef>();
            stairRef.Parameters = QdStair.ParamsDefinition.New();
            stairRef.StepHeight = 30;
            stairRef.StepWidth = 30;
            stairRef.StepNum = 12;
            stairRef.LadderWidth = 100;
            stairRef.PlatLen = 250;
            stairRef.PlatWidth = 100;
            stairRef.PlatThickness = 20;
            stairRef.SlabThickness = 20;
            stairRef.InsertPoint = instP;
            stairRef.Matrix = Matrix3d.Translate(instP);
            stairRef.stair = stair;
        End:
            this.PointInputer = null;
            this.EndCreating();
            this.DrawStair(stairRef);
        }
        public override void Draw(SKCanvas canvas, LcElement element, Vector2d offset)
        {
            var stairRef = element as QdStairRef;
            var curves = stairRef.ShapeCurves;
            var pen = this.GetDrawPen(element);
            if (pen == LightCAD.Runtime.Constants.defaultPen)
            {
                pen = new SKPaint { Color = SKColors.White, IsStroke = true };
            }
            DrawStairCurves(canvas, curves, stairRef.Matrix, pen);
        }
        public override void Draw(SKCanvas canvas, LcElement element, Matrix3d matrix)
        {
            var stairRef = element as QdStairRef;
            var curves = stairRef.ShapeCurves;
            var pen = this.GetDrawPen(element);
            if (pen == LightCAD.Runtime.Constants.defaultPen)
            {
                pen = new SKPaint { Color = SKColors.White, IsStroke = true };
            }
            matrix = matrix * stairRef.Matrix;
            DrawStairCurves(canvas, curves, matrix, pen);
        }
        public void DrawStairCurves(SKCanvas canvas, List<ICurve2d> curves, Matrix3d matrix, SKPaint pen) 
        {
            foreach (var curve in curves)
            {
                switch (curve.Type)
                {
                    case Curve2dType.Rect2d:
                        {
                            var rect = curve as Rect2d;
                            var min = rect.Min;
                            var max = rect.Max;
                            var a = this.vportRt.ConvertWcsToScr(matrix.MultiplyPoint(min)).ToSKPoint();
                            var b = this.vportRt.ConvertWcsToScr(matrix.MultiplyPoint(new Vector2d(max.X,min.Y))).ToSKPoint();
                            var c = this.vportRt.ConvertWcsToScr(matrix.MultiplyPoint(max)).ToSKPoint();
                            var d = this.vportRt.ConvertWcsToScr(matrix.MultiplyPoint(new Vector2d(min.X,max.Y))).ToSKPoint();
                            canvas.DrawPoints(SKPointMode.Polygon,new SKPoint[] { a, b, c, d,a }, pen);
                            break;
                        }
                    default:
                        break;
                }
            }
        }

        private void DrawStair(QdStairRef stairRef) 
        {
            if (stairRef == null)
                return;
            stairRef.IsFireChangedEvent = true;
            this.docRt.Document.ModelSpace.InsertElement(stairRef);
            this.docRt.Action.ClearSelects();
        }
        public override void Cancel()
        {
            base.Cancel();
            this.vportRt.SetCreateDrawer(null);
        }

        #region Grip
        private QdStairRef _stairRef;
        private string _gripName;
        private Vector2d _position;

        public override ControlGrip[] GetControlGrips(LcElement element)
        {
            var stairRef = element as QdStairRef;
            var grips = new List<ControlGrip>();

            var gripInsert = new ControlGrip
            {
                Element = stairRef,
                Name = "InsertPoint",
                Position = stairRef.InsertPoint
            };
            grips.Add(gripInsert);
            return grips.ToArray();
        }

        public override void SetDragGrip(LcElement element, string gripName, Vector2d position, bool isEnd)
        {
            var stairRef = element as QdStairRef;
            _stairRef = stairRef;
            if (!isEnd)
            {
                _gripName = gripName;
                _position = position;
            }
            else
            {
                if (gripName == "InsertPoint")
                {
                    var offset = position - stairRef.InsertPoint;
                    stairRef.InsertPoint = position;
                    stairRef.Matrix = Matrix3d.Translate(offset)*stairRef.Matrix ;
                }
            }

        }

        public override void DrawDragGrip(SKCanvas canvas)
        {
            if (_stairRef == null)
                return;
            if (_gripName == "InsertPoint")
            {
                var offset = _position - _stairRef.InsertPoint;
                var matrix = Matrix3d.Translate(offset) * _stairRef.Matrix;
                var curves = _stairRef.ShapeCurves;
                DrawStairCurves(canvas, curves, matrix, LightCAD.Runtime.Constants.draggingPen);
            }
         
        }
        #endregion

        public override LightCAD.Three.BufferGeometry[] CreateGeometry3D(LcElement element, Matrix3d matrix)
        {
            var stair = element as QdStairRef;
            var laddergeo = CreateLadder(stair.StepHeight, stair.StepNum, stair.StepWidth, stair.LadderWidth, stair.SlabThickness);
            var laddergeo2 = laddergeo.clone()
                .rotateZ(-MathUtil.Pi)
                .translateQuick(stair.PlatLen, stair.StepNum * stair.StepWidth, stair.StepNum * stair.StepHeight);

            var plat = CreatePlat(stair.PlatLen, stair.PlatWidth, stair.PlatThickness);
            plat.translateQuick(0, stair.StepNum * stair.StepWidth, stair.StepNum * stair.StepHeight);
            var result = new BufferGeometry[] {laddergeo,laddergeo2,plat };
            matrix = matrix * stair.Matrix;
            foreach (var geo in result)
            {
                GeoUtil.GeoApplyMatrix3d(geo, matrix);
                geo.computeVertexNormals();
            }
            return result;
        }
        private LightCAD.Three.BufferGeometry CreateLadder(double stepHeight,int stepNum,double stepWidth,double ladderLen,double slabThickness) 
        {
            var poly = new JsArr<Vector2>();
            for (int i = 0; i < stepNum; i++)
            {
                var x=i * stepWidth;
                var y=i * stepHeight;
                poly.push(new Vector2(x, y), new Vector2(x, y + stepHeight));
            }
            poly.push(new Vector2(stepNum * stepWidth, stepNum * stepHeight));
            var p0 = poly[0];
            var p2 = poly[2];
            var obliqueDir= p2.clone().sub(p0).normalize();
            var offsetDir= obliqueDir.clone().rotateAround(new Vector2(), -Math.PI / 2);
            var sp=new Vector2().addScaledVector(offsetDir,slabThickness );
            sp= CurveUtil.LineIntersectLine2D(sp, obliqueDir, p0, new Vector2(1, 0));
            var ep= CurveUtil.LineIntersectLine2D(sp, obliqueDir, poly.Last(),new Vector2(0,-1));
            poly.push(ep, sp);
            poly.Reverse();
            var shape = new Shape(poly);
            var coodMat = new Matrix4();
            coodMat.makeBasis(new Vector3(0,1,0), new Vector3(0,0,1), new Vector3(1,0,0));
            return GeoUtil.GetStretchGeometryData(shape, coodMat, ladderLen,0).GetBufferGeometry();
        }

        private LightCAD.Three.BufferGeometry CreatePlat(double len,double width,double thickness) 
        {
            var shape= ShapeUtil.Rect(0, 0, len, width);
            var coodMat= new Matrix4();
            coodMat.makeBasis(new Vector3(1,0,0), new Vector3(0,1,0),new Vector3(0,0,1));
            return GeoUtil.GetStretchGeometryData(shape, coodMat, 0, -thickness).GetBufferGeometry();
        }
    }
}
